TRACEMALLOC = False
SIM = False
import platform

from udp_cmd_worker import UDP_CMD_WORKER
import socket

if platform.system() == 'Windows':
    SIM = True

import asyncio
import logging
import os
import sys

from config import config

if SIM:
    from sim_fpga_worker import FPGA_WORKER
else:
    from fpga_worker import FPGA_WORKER

if TRACEMALLOC:
    import tracemalloc

from logging.handlers import TimedRotatingFileHandler


logger = logging.getLogger()

async def log_mem():
    while(True):
        if TRACEMALLOC:
            if tracemalloc.is_tracing():
                sn = tracemalloc.take_snapshot()
                top_stats = sn.statistics('lineno')
                logger.debug("[TRACEMALLOC TOP 3:]")
                for stat in top_stats[:3]:
                    logger.debug(stat) 
        
        await asyncio.sleep(1)


class FBGI():

    def __init__(self, cfg=None, loop=None) -> None:
        if cfg is None:
            self.cfg = config()
        else:
            self.cfg = cfg

        if loop is None:
            self.loop = asyncio.get_event_loop()
        else:
            self.loop = loop

        if SIM:
            self.cfg.host_ip = '127.0.0.1'
            self.cfg.udp_port = 8900
            self.cfg.tcp_port = 8902
            
        self.set_logger()
        
        self.__init_proc()

        self.__s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.__s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.__s.bind((self.cfg.host_ip, self.cfg.udp_port))

        self._eth = UDP_CMD_WORKER(self.cfg, self.__s)
        self._fpga = FPGA_WORKER(self.cfg, self.__s)
        # self._data_server = TCP_RESULT_SERVER(self.cfg, eth_send_q)

        # self._eth.add_periodic_generater(self._fpga.sample_one_bulk())

        self.workers = [self._eth, self._fpga,]
        self.tasks = [log_mem()]
        for worker in self.workers:
            self.tasks += worker.tasks
    

    def set_logger(self):
        log_fmt = self.cfg.log_fmt
        log_level = self.cfg.log_level
        log_file = self.cfg.log_file
        log_path = os.path.split(log_file)[0]
        os.makedirs(log_path, exist_ok=True)

        formatter = logging.Formatter(log_fmt)
        file_handler = TimedRotatingFileHandler(log_file, when='midnight', backupCount=10)
        con_handler = logging.StreamHandler(sys.stdout)
        file_handler.setFormatter(formatter)
        con_handler.setFormatter(formatter)
        file_handler.setLevel(log_level)
        con_handler.setLevel(logging.DEBUG)

        logger.addHandler(file_handler)
        logger.addHandler(con_handler)
        logger.setLevel(logging.DEBUG)


    def __set_host_ip_exe(self, ip=None, netmask=None, gateway=None):
        if ip is not None:
            ret = os.system(f'sudo ifconfig eth0 {ip}')
            if ret == 0:
                logger.info(f"Set host ip to {ip} successfully.")                       
            else:
                logger.warning(f"Set host ip to {ip} failed. Return value {ret}")        
                return False
        if netmask is not None:
            ret = os.system(f'sudo ifconfig eth0 netmask {netmask}')
            if ret == 0:
                logger.info(f"Set netmask to {netmask} successfully.")                       
            else:
                logger.warning(f"Set netmask to {netmask} failed. Return value {ret}")        
                return False
        if gateway is not None:
            ret = os.system(f'sudo route add default gw {gateway}')
            if ret == 0:
                logger.info(f"Set gateway to {gateway} successfully.")                       
            else:
                logger.warning(f"Set gateway to {gateway} failed. Return value {ret}")        
                return False
            return True

    def __init_proc(self):
        logger.info("Start initiaizing process...")
        cfg = self.cfg
        if platform.system() != 'Windows':
            self.__set_host_ip_exe(ip=cfg.host_ip, netmask=cfg.netmask, gateway=cfg.gateway)

    async def main(self):
        logger.info("="*70)
        logger.info("*"*24 + "  FBGI program start  " + "*"*24)
        logger.info("="*70)

        logger.info("Current workdir %s" % os.getcwd())

        logger.info("Using config file:")
        logger.info("%s" % self.cfg)

        if TRACEMALLOC:
            tracemalloc.start()

        await asyncio.gather(*self.tasks)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    fbgi = FBGI(loop=loop)
    try:
        loop.run_until_complete(loop.create_task(fbgi.main()))
        loop.run_forever()
    except BaseException as e:
        fbgi._fpga.clean()
        logger.critical(e, exc_info=True)
        logger.critical("Unexpeted error occurs. Exit ...")
        sys.exit(-1)